using System;
using System.Collections.Generic;
using System.Text;
using System.IO.Ports;
using System.IO;

namespace DTLAutomateGUI.DTLAutomate
{
	public delegate void CommErrorEventHandler(string message);

	public class APIControl : IDisposable
	{
		private const string defaultPortName = "COM1";

		private string portName = "com1";
		private DriverRS232 driver;
		private bool connected = false;

	#region public events

		public event ResponseCallback dataRecieved;
		public event CommErrorEventHandler commError;

		#endregion

	#region public properties

		public bool Connected
		{
			get { return this.connected; }
		}

		#endregion

	#region public methods

		public APIControl()
		{
			this.APIControl_(defaultPortName);
		}

		/// <summary>
		/// Creates an APIControl object on the port given.  This also creates a DriverRS232.
		/// </summary>
		/// <param name="portName">The name of the port</param>
		public APIControl(string portName)
		{
			this.APIControl_(portName);
		}

		private void APIControl_(string portName)
		{
			this.portName = portName;
			this.connect();
		}

		/// <summary>
		/// Tries to connect to the marklin interface.
		/// </summary>
		/// <returns>True if connected</returns>
		public bool connect()
		{
			if (this.driver != null)
			{
				try
				{
					this.driver.Dispose();
				}
				catch
				{
				}
			}

            try
            {
                this.driver = new DriverRS232(portName);
                this.driver.recieveError += new RS232Error(driver_recieveError);
                this.driver.start();
                this.connected = true;
            }
            catch (MarklinInterfaceNotRespondingException e)
            {
                this.connected = false;
                this.driver = null;
                return false;
            }
            catch (IOException e)
            {
                this.connected = false;
                if (this.driver != null)
                {
                    this.driver.Dispose();
                }
                this.driver = null;
                return false;
            }

			//if (this.connected)
			//{
			//    this.S88Timer = new Timer();
			//    this.S88Timer.Interval = 200;
			//    this.S88Timer.Tick += new EventHandler(S88Timer_Tick);
			//    this.S88Timer.Start();
			//}

			return this.connected;
		}

		internal void driver_recieveError(MarklinCommand c, string d, string message)
		{
			this.commError("Error Sending or Receiving command: " + c.command + ": " + c.ToString() + "\nResponse received: " + d + "\nError message: " + message);
		}

		/// <summary>
		/// Disconnects from the marklin interface
		/// </summary>
		/// <returns>False if disconnected</returns>
		public bool disconnect()
		{
			this.Dispose();
			return this.connected;
		}

		/// <summary>
		/// Controls a locomotive
		/// </summary>
		/// <param name="locoID">Locomotive Address/ID</param>
		/// <param name="speed">The desired speed</param>
		/// <param name="changeDir">if a change is direction is desired (speed set to zero)</param>
		/// <param name="function">if the function is on</param>
		/// <returns>true if successful</returns>
		public bool controlLocomotive(int locoID, int speed, bool changeDir, bool function)
		{
			if (!this.connected) return false;
			bool returnValue;
			string output = "l";
			output += locoID;
			if (!changeDir)
			{
				output += "s";
				output += speed;
				output += "f";
				output += (function ? 1 : 0);
			}
			else
			{
				output += "d";
			}

			try
			{
				this.driver.sendCommand(output);
			}
			catch
			{
				returnValue = false;
			}
			finally
			{
				returnValue = true;
			}
			return returnValue;
		}

		/// <summary>
		/// Send a command through the serial port to change a switch position
		/// </summary>
		/// <param name="switchID">Switch ID</param>
		/// <param name="switchPosition">True straight, False curved</param>
		/// <returns>true if successful</returns>
		public bool controllSwitch(int switchID, bool switchPosition)
		{
			if (!this.connected) return false;
			bool returnValue;
			string output = "m";
			output += switchID.ToString();
			output += (switchPosition ? "g" : "r");

			try
			{
				this.driver.sendCommand(output);
			}
			catch
			{
				returnValue = false;
			}
			finally
			{
				returnValue = true;
			}
			return returnValue;
		}

		/// <summary>
		/// Halts the layout
		/// </summary>
		/// <returns>true if successful</returns>
		public bool halt()
		{
			if (!this.connected) return false;
			bool returnValue;
			try
			{
				this.driver.sendCommand("S");
			}
			catch
			{
				returnValue = false;
			}
			finally
			{
				returnValue = true;
			}
			return returnValue;
		}

		/// <summary>
		/// Resumes the layout (power on)
		/// </summary>
		/// <returns>true if successful</returns>
		public bool release()
		{
			if (!this.connected) return false;
			bool returnValue;
			try
			{
				this.driver.sendCommand("G");
			}
			catch
			{
				returnValue = false;
			}
			finally
			{
				returnValue = true;
			}
			return returnValue;
		}

		#endregion

	#region protected methods
				
		/// <summary>
		/// Default Method for when data is received
		/// </summary>
		/// <param name="response">the received data</param>
		protected void APIControl_dataRecieved(string response)
		{
			DebugMethods.printDebug(response);
			return;
		}

		#endregion

	#region IDisposable methods
		
		public void Dispose()
		{
			if (this.driver != null) this.driver.Dispose();
			this.driver = null;
			this.connected = false;
		}

		#endregion

	}
}
